package com.imd.s9.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.imd.base.BaseMongoDao;
import com.imd.base.BusinessException;
import com.imd.base.ConditionsVO;
import com.imd.s9.entity.TableInfo;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Repository
@Slf4j
public class InitCriteria {
    @Resource
    BaseMongoDao baseMongoDao;

    @Resource
    MongoTemplate mongoTemplate;

    /**
     * 条件配置化
     * @param criteria
     * @param param
     * @return
     * @throws BusinessException
     */
    public Criteria appenCriteria(Criteria criteria,Object param,Criteria... criterias) throws BusinessException {
        if(param instanceof String){
            List<ConditionsVO.Condition> conditions = JSONArray.parseArray(param.toString(),ConditionsVO.Condition.class);
            if(conditions.isEmpty()){
                return criteria;
            }
            List<Criteria> criteriaList = baseMongoDao.condition2CriteriaList(conditions);
            criteria.andOperator(criteriaList);
        }
        if(param instanceof JSONObject){
            List<Criteria> criteriaList = appenCriteriaList(param,criterias);
            criteria.andOperator(criteriaList);
        }
        return criteria;
    }


    /**
     *
     * @param param
     * @return
     * @throws BusinessException
     */
    public  List<Criteria> appenCriteriaList(Object param,Criteria... criterias) throws BusinessException {
        List<Criteria> criteriaList = new ArrayList<>();
        JSONObject temp = (JSONObject) param;
        for(String key: temp.keySet()){
            if(!ObjectUtils.isEmpty(temp.get(key))){
                criteriaList.add(Criteria.where(key).is(temp.get(key)));
            }else {
                criteriaList.add(Criteria.where(key).isNull());
            }
        }
        if(criterias.length>0){
            List<Criteria> lists = Arrays.asList(criterias);
            criteriaList.addAll(lists);
        }
        return criteriaList;
    }

    public Criteria createCriteria(String tenantId,Criteria... cs){
        Criteria criteria = new Criteria();
        criteria.and("isDelete").is(0).and("tenantId").is(tenantId);
        List<Criteria> lists = Arrays.asList(cs);
        if(lists!=null&&!lists.isEmpty()){
            criteria.andOperator(lists);
        }
        return criteria;
    }

    public Query create(String tenantId,Criteria... cs) {
        Criteria criteria = createCriteria(tenantId,cs);
        Query cond = new Query();
        cond.addCriteria(criteria);
        return cond;
    }

    public Query createQuery(Criteria... cs){
        return create(TableInfo.tenantId,cs);
    }

    public Query jsonObjectToCriteria(JSONObject object) throws BusinessException{
        Criteria criteria = Criteria.where("isDelete").is(0).
                and("tenantId").is(TableInfo.tenantId);
        List<Criteria> criteriaList =new ArrayList<>();
        for(String key:object.keySet()){
            if(!ObjectUtils.isEmpty(object.get(key))){
                criteriaList.add(Criteria.where(key).is(object.get(key).toString().trim()));
            }else {
                criteriaList.add(Criteria.where(key).isNull());
            }
        }
        criteria.andOperator(criteriaList);
        Query query = new Query();
        query.addCriteria(criteria);
        return query;
    }

    /**
     * 查询指标数据
     *
     * @param criteria
     * @param vdoStrs
     * @return
     */
    public List<Document> getMetricData(Criteria criteria, String[] filterProjects,String[] showProjects, SortOperation sortOperation,
                                         String[] vdoStrs, String tableName,String optimizeType,
                                        String calculator,UnwindOperation... unwindOperations) {
        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(criteria));
        ProjectionOperation projectionOperation = Aggregation.project(filterProjects);
        operations.add(projectionOperation);
        for(int i =0;i<unwindOperations.length;i++){
           operations.add(unwindOperations[i]);
        }

        GroupOperation groupOperation = Aggregation.group(vdoStrs);
                switch (optimizeType) {
                    case "sum":
                        groupOperation = groupOperation.sum(calculator).as("数量");
                        break;
                    case "count":
                        groupOperation = groupOperation.count().as("数量");
                        break;
                    case "max":
                        groupOperation = groupOperation.max(calculator).as(vdoStrs[0]);
                        break;
                    case "min":
                        groupOperation = groupOperation.min(calculator).as(vdoStrs[0]);
                        break;
                    case "avg":
                        groupOperation = groupOperation.avg(calculator).as(vdoStrs[0]);
                        break;
                    default:
                }
        operations.add(groupOperation);
        ProjectionOperation projectionOperation1 = Aggregation.project(showProjects);
        if (vdoStrs.length == 1) {
            AddFieldsOperation addFieldsOperation = Aggregation.addFields().addFieldWithValue(showProjects[0], "$_id").build();
            operations.add(addFieldsOperation);
            projectionOperation1 = projectionOperation1.andInclude(showProjects[0]);
        }
        else {
            for (String vdo : showProjects) {
                AggregationExpression expression = ConditionalOperators.IfNull.ifNull(vdo).then(null);
                projectionOperation1 = projectionOperation1.and(expression).as(vdo);
            }
        }
        operations.add(projectionOperation1);
        ProjectionOperation projectionOperation2 = Aggregation.project(showProjects);
        for (String vdo : showProjects) {
            AggregationExpression expression = ConditionalOperators.IfNull.ifNull(vdo).then(null);
            projectionOperation2 = projectionOperation2.and(expression).as(vdo);

        }
        operations.add(projectionOperation2);

        // 如果没有顺序条件，使用兜底的排序。用 vdoStrs 字段升序排序
        if(sortOperation == null && vdoStrs.length > 0) {
            sortOperation = Aggregation.sort(Sort.Direction.ASC, showProjects[0]);
        }
        if(sortOperation != null) {
            operations.add(sortOperation);
        }
        Aggregation agg = Aggregation.newAggregation(operations);
        AggregationResults<Document> results = mongoTemplate.aggregate(agg, tableName, Document.class);
        return new ArrayList<>(results.getMappedResults());
    }


}
